package turtle.system.pt;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Properties;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;


public class PriceData {
  public static final int STRATEGY_CONSERVATIVE = 1;
  public static final int STRATEGY_POSITIVE = 2;
  public static final String CONS_FN = "data/conservative_pt.log";
  public static final String POSI_FN = "data/positive_pt.log";
  public String finalSuggestion = null;
  public String summary = null;
  private BufferedWriter writer;
  private static Logger DEFAULT_LOGGER =  loadDefaultLogger() ;
  public Logger logger = DEFAULT_LOGGER;
  
  //private static String DATA_PATH = "data/";

  //private static String FILENAME_SUFFIX = ".txt";

   private static boolean IS_PRINT_TURTLE_ACTION = true; // 印出每筆進出場記錄
//  private static boolean IS_PRINT_TURTLE_ACTION = false; // 印出每筆進出場記錄
  
   private static boolean IS_GENERATE_GRID_CSV = false; // 產生累積獲利 CSV 檔 // 約需執行 5 分鐘
//private static boolean IS_GENERATE_GRID_CSV = true; // 產生累積獲利 CSV 檔 // 約需執行 5 分鐘

  private static boolean IS_PRINT_TURTLE_SUMMARY = true; // 印出累積獲利摘要統計

  private static int SCALE_DOWN_FACTOR = 100; // 獲利 CSV 檔中數值除以此常數，方便作圖
// private static int[] LOSS_STOP_RANGE_FOR_LONG = {27,27}; // 做多停損點數實驗區間
//  private static int[] LOSS_STOP_RANGE_FOR_LONG = { 10, 90 }; // 做多停損點數實驗區間
  public static int[] LOSS_STOP_RANGE_FOR_LONG = null;
  public static int[] LOSS_STOP_RANGE_FOR_LONG_CONSERVATIVE = { 10, 10 }; // 做多停損點數實驗區間
  

  public static int[] LOSS_STOP_RANGE_FOR_LONG_POSITIVE = { 31, 31 }; // 做多停損點數實驗區間
  
//   private static int[] LOSS_STOP_RANGE_FOR_SHORT = {27,27}; // 做空停損點數實驗區間
//  private static int[] LOSS_STOP_RANGE_FOR_SHORT = { 10, 90 }; // 做空停損點數實驗區間
  public static int[] LOSS_STOP_RANGE_FOR_SHORT = null;
  public static int[] LOSS_STOP_RANGE_FOR_SHORT_CONSERVATIVE = { 31, 31 }; // 做空停損點數實驗區間
  
  public static int[] LOSS_STOP_RANGE_FOR_SHORT_POSITIVE = { 87, 87 }; // 做空停損點數實驗區間

//int minDaysIn = 1, maxDaysIn = 100, minDaysOut = 1, maxDaysOut = 100;
  private static int minDaysInForLong = 2, maxDaysInForLong = 30, minDaysOutForLong = 2, maxDaysOutForLong = 30;
  private static int minDaysInForShort = 2, maxDaysInForShort = 30, minDaysOutForShort = 2, maxDaysOutForShort = 30;

  public static int[][] inAndOut = null;
  public static int[][] inAndOut_CONSERVATIVE = new int[][] { // 特別觀察組合
        {3,16,15,22}  //保守穩健系統
	
	  
  };
  public static int[][] inAndOut_POSITIVE = new int[][] { // 特別觀察組合
	    {9,21,3,5}  //積極進取系統
	  
  };
  public String getFinalSuggestion() {
	  return finalSuggestion;
  }
  public String getSummary() {
	  return summary;
  }
  public PriceData(int strategyType, String BASE_FILENAME, String TX_FILENAME) throws IOException {
	  if (strategyType == STRATEGY_POSITIVE) {
		  LOSS_STOP_RANGE_FOR_LONG = LOSS_STOP_RANGE_FOR_LONG_POSITIVE;
		  LOSS_STOP_RANGE_FOR_SHORT = LOSS_STOP_RANGE_FOR_SHORT_POSITIVE;
		  inAndOut = inAndOut_POSITIVE;
		  writer = new BufferedWriter(new FileWriter(POSI_FN));
	  }
	  else {
		  LOSS_STOP_RANGE_FOR_LONG = LOSS_STOP_RANGE_FOR_LONG_CONSERVATIVE;
		  LOSS_STOP_RANGE_FOR_SHORT = LOSS_STOP_RANGE_FOR_SHORT_CONSERVATIVE;
		  inAndOut = inAndOut_CONSERVATIVE;
		  writer = new BufferedWriter(new FileWriter(CONS_FN));
	  }
	  
	  DateFormat df = new SimpleDateFormat("yyyyMMdd");
	  Date today = GregorianCalendar.getInstance().getTime();
	  
      long startTime = System.currentTimeMillis();

    String filename = BASE_FILENAME;

    FileReader reader = new FileReader(filename);
    BarSet barSet = new BarSet(reader);
    // Chart.draw(barSet);
    
    String filename2 = TX_FILENAME;
    FileReader reader2 = new FileReader(filename2);
    BarSet barSet2 = new BarSet(reader2);
    
    

    // print day records in which range is over 300
    // bigRange(barSet, 300);

    int maxProfitDaysInForLong = 0, maxProfitDaysOutForLong = 0,
        maxProfitDaysInForShort = 0, maxProfitDaysOutForShort = 0,
        maxProfitLossStopForLong = LOSS_STOP_RANGE_FOR_LONG[0], maxProfitLossStopForShort = LOSS_STOP_RANGE_FOR_SHORT[0];
    // csv file for Excel graphing
    if (IS_GENERATE_GRID_CSV) {
      BufferedWriter csvOut = new BufferedWriter(new FileWriter(BASE_FILENAME + ".csv"));
      StringBuilder sb = new StringBuilder();
      StatCalc scStrategyProfit = new StatCalc(); // 記錄所有進出天數組合的獲利

      int kStep = 7;
      int qStep = 7;
      for (int k = LOSS_STOP_RANGE_FOR_LONG[0]; k <= LOSS_STOP_RANGE_FOR_LONG[1]; k+=kStep) {
        for (int q = LOSS_STOP_RANGE_FOR_SHORT[0]; q <= LOSS_STOP_RANGE_FOR_SHORT[1]; q+=qStep) {
          for (int i = minDaysInForLong; i <= maxDaysInForLong; i++) {
            for (int j = minDaysOutForLong; j <= maxDaysOutForLong; j++) {
              for (int m = minDaysInForShort; m <= maxDaysInForShort; m++) {
                for (int n = minDaysOutForShort; n <= maxDaysOutForShort; n++) {
              double profit = turtle(barSet, i, j, m, n, k, q, barSet2);
              scStrategyProfit.enter(profit, new int[] { i, j, m, n, k, q });
              // int scaleProfit = (int)(profit /
              // SCALE_DOWN_FACTOR);
              // sb.append(String.format("%d", scaleProfit));
              // if (j != maxDaysOut) sb.append(","); // row end
              // 不加 ,
                }
              }
            }
            // sb.append("\n");
          }
          
          System.out
              .println(String
                  .format(
                      "完成停損%d,%d - 最大獲利組合(做多%d,%d)(做空%d,%d)[停%d,%d]=%.2f,最小獲利組合(做多%d,%d)(做空%d,%d)[停%d,%d]=%.2f,平均獲利=%.2f,正獲利組合數=%d(平均%.2f),負獲利組合數=%d(平均%.2f)",
                      k, q, 
                      scStrategyProfit.getMaxParam()[0], 
                      scStrategyProfit.getMaxParam()[1], 
                      scStrategyProfit.getMaxParam()[2],
                      scStrategyProfit.getMaxParam()[3], 
                      scStrategyProfit.getMaxParam()[4], 
                      scStrategyProfit.getMaxParam()[5], 
                      scStrategyProfit.getMax(), 
                      scStrategyProfit.getMinParam()[0],
                      scStrategyProfit.getMinParam()[1], 
                      scStrategyProfit.getMinParam()[2], 
                      scStrategyProfit.getMinParam()[3], 
                      scStrategyProfit.getMinParam()[4], 
                      scStrategyProfit.getMinParam()[5], 
                      scStrategyProfit.getMin(), 
                      scStrategyProfit.getMean(),
                      scStrategyProfit.getPositiveCount(), 
                      scStrategyProfit.getPositiveMean(), 
                      scStrategyProfit.getNegativeCount(), 
                      scStrategyProfit.getNegativeMean()));
        }
      }
      csvOut.write(sb.toString());
      csvOut.close();
      System.out
          .println(String
              .format(
                  "最大獲利組合(做多%d,%d)(做空%d,%d)[停%d,%d]=%.2f,最小獲利組合(做多%d,%d)(做空%d,%d)[停%d,%d]=%.2f,平均獲利=%.2f,正獲利組合數=%d(平均%.2f),負獲利組合數=%d(平均%.2f)",
                  scStrategyProfit.getMaxParam()[0], 
                  scStrategyProfit.getMaxParam()[1], 
                  scStrategyProfit.getMaxParam()[2], 
                  scStrategyProfit.getMaxParam()[3],
                  scStrategyProfit.getMaxParam()[4], 
                  scStrategyProfit.getMaxParam()[5], 
                  scStrategyProfit.getMax(), 
                  scStrategyProfit.getMinParam()[0],
                  scStrategyProfit.getMinParam()[1], 
                  scStrategyProfit.getMinParam()[2], 
                  scStrategyProfit.getMinParam()[3],
                  scStrategyProfit.getMinParam()[4], 
                  scStrategyProfit.getMinParam()[5], 
                  scStrategyProfit.getMin(),
                  scStrategyProfit.getMean(), 
                  scStrategyProfit.getPositiveCount(), 
                  scStrategyProfit.getPositiveMean(),
                  scStrategyProfit.getNegativeCount(), 
                  scStrategyProfit.getNegativeMean()));
      
      maxProfitDaysInForLong = scStrategyProfit.getMaxParam()[0];
      maxProfitDaysOutForLong = scStrategyProfit.getMaxParam()[1];
      maxProfitLossStopForLong = scStrategyProfit.getMaxParam()[2];
      maxProfitLossStopForShort = scStrategyProfit.getMaxParam()[3];
    }

    System.out.println("");
    for (int[] ia : inAndOut) {
      turtle(barSet, ia[0], ia[1], ia[2], ia[3], maxProfitLossStopForLong, maxProfitLossStopForShort, barSet2);
      System.out.println("");
    }

    //重新 print 一次最大獲利組合
    if (IS_GENERATE_GRID_CSV) {
      turtle(barSet, maxProfitDaysInForLong, maxProfitDaysOutForLong, maxProfitDaysInForShort, maxProfitDaysOutForShort, maxProfitLossStopForLong, maxProfitLossStopForShort, barSet2);
    }

    // Get elapsed time in milliseconds
    long elapsedTimeMillis = System.currentTimeMillis() - startTime;
    System.out
        .println(String.format("執行時間: %.2f 秒", elapsedTimeMillis / 1000F));
    writer.close();

  }
  public static void copyFile(String srcFN, String destFN) {	  
	  try {
	    File inputFile = new File(srcFN);
	    File outputFile = new File(destFN);

	    FileReader in = new FileReader(inputFile);
	    FileWriter out = new FileWriter(outputFile);
	    int c;

	    while ((c = in.read()) != -1)
	      out.write(c);

	    in.close();
	    out.close();
	  } catch (IOException e) {
		  System.out.println(e);
	  }
  }
  public static void bigRange(BarSet barSet, int howBig) {
    ListIterator<Bar> it = barSet.iter();
    int barCountTotal = 0;
    int barCountMatch = 0;
    while (it.hasNext()) {
      Bar bar = it.next();
      barCountTotal++;
      if (bar.range > howBig) {
        System.out.println(bar);
        barCountMatch++;
      }
    }
    System.out.println(String.format("%d out of %d match the criteria",
        barCountMatch, barCountTotal));
  }

  private static int highestIndex(List<Double> list) {
    int ret = 0;
    Iterator<Double> iter = list.iterator();
    double highest = Double.MIN_VALUE;
    int currentIndex = 0;
    while (iter.hasNext()) {
      double currentValue = iter.next();
      if (currentValue > highest) {
        highest = currentValue;
        ret = currentIndex;
      }
      currentIndex++;
    }
    return ret;
  }

  private static int lowestIndex(List<Double> list) {
    int ret = 0;
    Iterator<Double> iter = list.iterator();
    double lowest = Double.MAX_VALUE;
    int currentIndex = 0;
    while (iter.hasNext()) {
      double currentValue = iter.next();
      if (currentValue < lowest) {
        lowest = currentValue;
        ret = currentIndex;
      }
      currentIndex++;
    }
    return ret;
  }

  private String printTurtleAction(Bar bar, boolean inHand, boolean bull,
      double baseOrProfit, int daysInOrOut, boolean stopLoss, int lossStop,
      double liquidPrice, Bar bar2, Bar breakBar2) {
    if (!IS_PRINT_TURTLE_ACTION)
      return null;
    StringBuffer buf = new StringBuffer();

    buf.append(bar.mergeBar(bar2)+"\n");
    buf.append((inHand ? "進場" : ("出場, 出場價 " + String.format("%.0f", liquidPrice)))
        + ", 理由："
        + (stopLoss ? ("停損 " + lossStop + " 點    ")
            : ("前一日創 " + daysInOrOut + " 天新" + (inHand ^ bull ? "低" : "高")))
        + ", ");
    
    buf.append(String.format("前一日[%.0f-%.0f-%.0f-%.0f-d=%.0f], ", breakBar2.open, breakBar2.high, breakBar2.low, breakBar2.close, breakBar2.diff));
    if (inHand) {    
      buf.append("做" + (bull ? "多" : "空") + ", 進場價 " + String.format("%.0f", baseOrProfit));
    } else {
      buf.append("獲利 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *["
              + String.format("%.0f", baseOrProfit) + "] * *");
    }
    try {
		writer.write(buf.toString()+"\n");
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
    logger.info(buf.toString());
/*    
    logger.info(bar.mergeBar(bar2));
    buf.append(bar.mergeBar(bar2));
    logger.info ((inHand ? "進場" : ("出場, 出場價 " + String.format("%.0f", liquidPrice)))
        + ", 理由："
        + (stopLoss ? ("停損 " + lossStop + " 點    ")
            : ("前一日創 " + daysInOrOut + " 天新" + (inHand ^ bull ? "低" : "高")))
        + ", ");
    buf.append((inHand ? "進場" : ("出場, 出場價 " + String.format("%.0f", liquidPrice)))
        + ", 理由："
        + (stopLoss ? ("停損 " + lossStop + " 點    ")
            : ("前一日創 " + daysInOrOut + " 天新" + (inHand ^ bull ? "低" : "高")))
        + ", ");
    logger.info(String.format("前一日[%.0f,%.0f,%.0f,%.0f,d=%.0f], ", breakBar2.open, breakBar2.high, breakBar2.low, breakBar2.close, breakBar2.diff));
    buf.append(String.format("前一日[%.0f,%.0f,%.0f,%.0f,d=%.0f], ", breakBar2.open, breakBar2.high, breakBar2.low, breakBar2.close, breakBar2.diff));
    if (inHand) {
    	logger.info("做" + (bull ? "多" : "空") + ", 進場價 " + String.format("%.0f", baseOrProfit));
      buf.append("做" + (bull ? "多" : "空") + ", 進場價 " + String.format("%.0f", baseOrProfit));
    } else {
    	logger.info("獲利 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *["
              + String.format("%.0f", baseOrProfit) + "] * *");
      buf.append("獲利 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *["
              + String.format("%.0f", baseOrProfit) + "] * *");
    }
*/    
    System.out.println("=======================================");
    finalSuggestion = buf.toString();
    return buf.toString();
  }

  private static int diffCalendarDays(Bar current, Bar base) {
    long diffMillis = current.today.getTimeInMillis()
        - base.today.getTimeInMillis();
    return (int) (diffMillis / (24 * 60 * 60 * 1000));
  }
  StatCalc thisProfit = null;
  public void setProfitStat(StatCalc prof) {
	  this.thisProfit = prof;
  }
  public StatCalc getProfitStat() {
	  return thisProfit;
  }
  double thisAcc = -1;
  public void setAcc(double acc) {
	  this.thisAcc = acc;
  }
  public double getAcc() {
	  return this.thisAcc;
  }
  MDD thisMDD = null;
  public void setMDD(MDD mdd) {
	  this.thisMDD = mdd;
  }
  public MDD getMDD() {
	  return this.thisMDD;
  }
  
  public double turtle(BarSet barSet, int daysInForLong, int daysOutForLong, int daysInForShort, int daysOutForShort,
      int lossStopForLong, int lossStopForShort, BarSet barSet2) {

    ListIterator<Bar> it = barSet.iter();
    Bar baseBar = null;
    int baseBarIndex = 0;
    StatCalc scHoldCalendarDays = new StatCalc();
    StatCalc scHoldTransactionDays = new StatCalc();
    StatCalc scProfit = new StatCalc();
    StatCalc scProfitLongDayTrade = new StatCalc();
    StatCalc scProfitShortDayTrade = new StatCalc();
    StatCalc scProfitLongCloseDayTrade = new StatCalc();
    StatCalc scProfitShortCloseDayTrade = new StatCalc();
    StatCalc scProfitLong = new StatCalc();
    StatCalc scProfitShort = new StatCalc();
    StatCalc maxDrawDown = new StatCalc();
    
    boolean inHand = false;
    boolean bull = false;
    double basePrice = 0.0;
    double basePrice2 = 0.0;
    double accuProfit = 0.0;

    LinkedList<Double> listHighInForLong = new LinkedList<Double>(); // 以各 day bar 的 high 組成的 list，用以評估進場點(做多)
    LinkedList<Double> listLowInForLong = new LinkedList<Double>(); // 以各 day bar 的 low 組成的 list，用以評估進場點(做多)
    LinkedList<Double> listHighOutForLong = new LinkedList<Double>(); // 以各 day bar 的 high 組成的 list，用以評估出場點(做多)
    LinkedList<Double> listLowOutForLong = new LinkedList<Double>(); // 以各 day bar 的 low 組成的 list，用以評估出場點(做多)
    LinkedList<Double> listCloseInForLong = new LinkedList<Double>(); // 以各 day bar 的 close 組成的 list，用以評估進場點(做多)
    LinkedList<Double> listCloseOutForLong = new LinkedList<Double>(); // 以各 day bar 的 close 組成的 list，用以評估出場點(做多)

    LinkedList<Double> listHighInForShort = new LinkedList<Double>(); // 以各 day bar 的 high 組成的 list，用以評估進場點(做空)
    LinkedList<Double> listLowInForShort = new LinkedList<Double>(); // 以各 day bar 的 low 組成的 list，用以評估進場點(做空)
    LinkedList<Double> listHighOutForShort = new LinkedList<Double>(); // 以各 day bar 的 high 組成的 list，用以評估出場點(做空)
    LinkedList<Double> listLowOutForShort = new LinkedList<Double>(); // 以各 day bar 的 low 組成的 list，用以評估出場點(做空)
    LinkedList<Double> listCloseInForShort = new LinkedList<Double>(); // 以各 day bar 的 close 組成的 list，用以評估進場點(做空)
    LinkedList<Double> listCloseOutForShort = new LinkedList<Double>(); // 以各 day bar 的 close 組成的 list，用以評估出場點(做空)

    while (it.hasNext()) {
      /* 取得新一天的資料 */
      Bar bar = it.next();

      /* 更新進場點資料 window (做多) */
      if (listHighInForLong.size() == daysInForLong) {
        listHighInForLong.removeFirst();
      }
      if (listLowInForLong.size() == daysInForLong) {
        listLowInForLong.removeFirst();
      }
      if (listCloseInForLong.size() == daysInForLong) {
        listCloseInForLong.removeFirst();
      }
      listHighInForLong.add(bar.high);
      listLowInForLong.add(bar.low);
      listCloseInForLong.add(bar.close);

      /* 更新進場點資料 window (做空) */
      if (listHighInForShort.size() == daysInForShort) {
        listHighInForShort.removeFirst();
      }
      if (listLowInForShort.size() == daysInForShort) {
        listLowInForShort.removeFirst();
      }
      if (listCloseInForShort.size() == daysInForShort) {
        listCloseInForShort.removeFirst();
      }
      listHighInForShort.add(bar.high);
      listLowInForShort.add(bar.low);
      listCloseInForShort.add(bar.close);

      /* 更新出場點資料 window (做多) */
      if (listHighOutForLong.size() == daysOutForLong) {
        listHighOutForLong.removeFirst();
      }
      if (listLowOutForLong.size() == daysOutForLong) {
        listLowOutForLong.removeFirst();
      }
      if (listCloseOutForLong.size() == daysOutForLong) {
        listCloseOutForLong.removeFirst();
      }
      listHighOutForLong.add(bar.high);
      listLowOutForLong.add(bar.low);
      listCloseOutForLong.add(bar.close);

      /* 更新出場點資料 window (做空) */
      if (listHighOutForShort.size() == daysOutForShort) {
        listHighOutForShort.removeFirst();
      }
      if (listLowOutForShort.size() == daysOutForShort) {
        listLowOutForShort.removeFirst();
      }
      if (listCloseOutForShort.size() == daysOutForShort) {
        listCloseOutForShort.removeFirst();
      }
      listHighOutForShort.add(bar.high);
      listLowOutForShort.add(bar.low);
      listCloseOutForShort.add(bar.close);

      if (!inHand) { // 目前空手
        if ((listLowInForShort.size() == daysInForShort && lowestIndex(listLowInForShort) == daysInForShort - 1)
            && (listHighInForLong.size() == daysInForLong && highestIndex(listHighInForLong) == daysInForLong - 1)) { //盤中 創 daysInForShort 天新低且創 daysInForLong 天新高(盤中雙向突破）
          // do nothing, 不進場
        } else if (listLowInForShort.size() == daysInForShort
        // && lowestIndex(listHighInForShort) == daysInForShort - 1 // day bar 的 high 創 daysInForShort 天新低
            && lowestIndex(listLowInForShort) == daysInForShort - 1 // day bar 的 low 創 daysInForShort 天新低 ****** 略勝
            // && lowestIndex(listCloseInForShort) == daysInForShort - 1 // day bar 的 close 創 daysInForShort 天新低
        ) {
          inHand = true; // (空單)進場
          bull = false; // 建立 short position
          int nextIndex = it.nextIndex();
          if (nextIndex == barSet.size())
            continue; // 解決在資料尾端會造成 IndexOutOfBoundsException 的問題
          
          Bar nextBar = barSet.getBar(nextIndex);  // 交易 DATA_FILENAME
          Bar nextBar2 = barSet2.getBar(nextIndex);  // 交易 DATA_FILENAME2
          Bar breakBar2 = barSet2.getBar(nextIndex - 1);
          
          basePrice = nextBar.open; // 假設隔天開盤價成交
//           basePrice = nextBar.close; // 假設隔天收盤價成交
          basePrice2 = nextBar2.open; // 假設隔天開盤價成交
//          basePrice2 = nextBar2.close; // 假設隔天開盤價成交
          
//          double temp = nextBar.open - nextBar.close;  // 做空訊號指示進場當天，開盤加倍放空，收盤回補一半，留倉正常風險量，持續做空****** 賺很多
          double temp2 = nextBar2.open - nextBar2.close;
//          accuProfit += temp;
          accuProfit += temp2;                                                       
//          scProfitShortDayTrade.enter(temp);
          scProfitShortDayTrade.enter(temp2);

          baseBar = nextBar;
          baseBarIndex = it.nextIndex();
//          printTurtleAction(nextBar, inHand, bull, basePrice, daysIn, false, 0, 0);
          printTurtleAction(nextBar, inHand, bull, basePrice2, daysInForShort, false, 0, 0, nextBar2, breakBar2);          
        } else if (listHighInForLong.size() == daysInForLong
        // && highestIndex(listHighInForLong) == daysInForLong - 1 // day bar 的 high 創 daysInForLong 天新高
            && highestIndex(listLowInForLong) == daysInForLong - 1 // day bar 的 low 創 daysInForLong 天新高
            // && highestIndex(listCloseInForLong) == daysInForLong - 1 // day bar 的 close 創 daysInForLong 天新高
        ) {
          inHand = true; // 進場
          bull = true; // 建立 long position
          int nextIndex = it.nextIndex();
          if (nextIndex == barSet.size())
            continue; // 解決在資料尾端會造成 IndexOutOfBoundsException 的問題
          
          Bar nextBar = barSet.getBar(nextIndex);  // 交易 DATA_FILENAME
          Bar nextBar2 = barSet2.getBar(nextIndex);  // 交易 DATA_FILENAME2
          Bar breakBar2 = barSet2.getBar(nextIndex - 1);
          
//           basePrice = nextBar.open; // 假設隔天開盤價成交  // 判斷進出場價
          basePrice = nextBar.close; // 假設隔天收盤價成交  // 判斷進出場價
//          basePrice2 = nextBar2.close; // 假設隔天收盤價成交  // 實際操作價
          basePrice2 = nextBar2.open; // 假設隔天收盤價成交  // 實際操作價

//          double temp = nextBar.open - nextBar.close;  // 做多訊號指示進場當天，開盤放空，收盤回補，並做多 *************** 賺不多，但逾  5 成勝率 
          double temp2 = nextBar2.open - nextBar2.close;  // 做多訊號指示進場當天，開盤放空，收盤回補，並做多 *************** 用期貨做會賠錢……
//          accuProfit += temp;                                           
          accuProfit += temp2;                                           
//          scProfitLongDayTrade.enter(temp);
          scProfitLongDayTrade.enter(temp2);

          baseBar = nextBar;
          baseBarIndex = it.nextIndex();
//          printTurtleAction(nextBar, inHand, bull, basePrice, daysIn, false, 0, 0);
          printTurtleAction(nextBar, inHand, bull, basePrice2, daysInForLong, false, 0, 0, nextBar2, breakBar2);
        }
      } else { // 目前手上有部位
        if (listHighOutForShort.size() == daysOutForShort
            && highestIndex(listHighOutForShort) == daysOutForShort - 1 // day bar 的 high 創 daysOutForShort 天的 high 新高
            // && highestIndex(listCloseOutForShort) == daysOutForShort - 1 // day bar 的 close 創 daysOutForShort 天 的 close 新高
            // && highestIndex(listLowOutForShort) == daysOutForShort - 1 // day bar 的 low 創 daysOutForShort 天 的 low 新高
            && !bull) { // 手上持有 short position
          inHand = false; // (空單)出場
          int nextIndex = it.nextIndex();
          if (nextIndex == barSet.size())
            continue; // 解決在資料尾端會造成 IndexOutOfBoundsException 的問題
          
          Bar nextBar = barSet.getBar(nextIndex);  // 交易 DATA_FILENAME
          Bar nextBar2 = barSet2.getBar(nextIndex);  // 交易 DATA_FILENAME2
          Bar breakBar2 = barSet2.getBar(nextIndex - 1);

//          double liquidatePrice = nextBar.close; // 不用分損益，都是用 close 平倉最好
//          double liquidatePrice2 = nextBar2.close; // 不用分損益，都是用 close 平倉最好
          double liquidatePrice2 = nextBar2.open; // 不用分損益，都是用 open 平倉最好
          // if (basePrice > bar.high) { // 做空獲利
          // liquidatePrice = nextBar.open; // 假設隔天開盤價成交(空單平倉)
          // -----------------------------------------------------------------
          // } else { // 做空虧損
          // liquidatePrice = nextBar.close; // 假設隔天收盤價成交(空單平倉)
          // }

//          double profit = basePrice - liquidatePrice; // 計算獲利
          double profit2 = basePrice2 - liquidatePrice2; // 計算獲利
//          accuProfit += profit;
          accuProfit += profit2;
//          scProfit.enter(profit);
          scProfit.enter(profit2);
//          scProfitShort.enter(profit);
          scProfitShort.enter(profit2);

          scHoldCalendarDays.enter(diffCalendarDays(nextBar, baseBar));
          scHoldTransactionDays.enter(it.nextIndex() - baseBarIndex);
//          printTurtleAction(nextBar, inHand, bull, profit, daysOut, false, 0, liquidatePrice);
          printTurtleAction(nextBar, inHand, bull, profit2, daysOutForShort, false, 0, liquidatePrice2, nextBar2, breakBar2);
        } else if (listLowOutForLong.size() == daysOutForLong
            && lowestIndex(listLowOutForLong) == daysOutForLong - 1 // 創 daysOutForLong 天新低
            // && lowestIndex(listHighOutForLong) == daysOutForLong - 1 // 創 daysOutForLong 天新低
            // && lowestIndex(listCloseOutForLong) == daysOutForLong - 1 // 創 daysOutForLong 天新低
            // **************** 有機會，試所有天數、停損組合 ******
            && bull) { // 手上持有 long position
          inHand = false; // 出場
          int nextIndex = it.nextIndex();
          if (nextIndex == barSet.size())
            continue; // 解決在資料尾端會造成 IndexOutOfBoundsException 的問題
          
          Bar nextBar = barSet.getBar(nextIndex);  // 交易 DATA_FILENAME
          Bar nextBar2 = barSet2.getBar(nextIndex);  // 交易 DATA_FILENAME2
          Bar breakBar2 = barSet2.getBar(nextIndex - 1);
          
          double liquidatePrice;
          if (basePrice < bar.low) { // 做多獲利
            liquidatePrice = nextBar.open; // 假設隔天開盤價成交(多單平倉)
          } else { // 做多虧損
            liquidatePrice = nextBar.close; // 假設隔天收盤價成交(多單平倉)
          }
          
          double liquidatePrice2;
          if (basePrice < bar.low) { // 做多獲利
//            liquidatePrice2 = nextBar2.open; // 假設隔天開盤價成交(多單平倉)
            liquidatePrice2 = nextBar2.close; // 假設隔天開盤價成交(多單平倉)
          } else { // 做多虧損
//            liquidatePrice2 = nextBar2.close; // 假設隔天收盤價成交(多單平倉)
            liquidatePrice2 = nextBar2.open; // 假設隔天收盤價成交(多單平倉)
          }
          
//          double profit = liquidatePrice - basePrice; // 計算獲利
          double profit2 = liquidatePrice2 - basePrice2; // 計算獲利
//          accuProfit += profit;
          scProfit.enter(profit2);
//          accuProfit += profit;
          scProfitLong.enter(profit2);
//          accuProfit += profit;
          accuProfit += profit2;
          
          scHoldCalendarDays.enter(diffCalendarDays(nextBar, baseBar));
          scHoldTransactionDays.enter(it.nextIndex() - baseBarIndex);
//          printTurtleAction(nextBar, inHand, bull, profit, daysOut, false, 0, liquidatePrice);
          printTurtleAction(nextBar, inHand, bull, profit2, daysOutForLong, false, 0, liquidatePrice2, nextBar2, breakBar2);          
        } else if (bar.close >= basePrice + lossStopForShort // 停損
            && !bull) { // 手上持有 short position = 做空
          inHand = false; // 出場
          int nextIndex = it.nextIndex();
          if (nextIndex == barSet.size())
            continue; // 解決在資料尾端會造成 IndexOutOfBoundsException 的問題
          
          Bar nextBar = barSet.getBar(nextIndex);  // 交易 DATA_FILENAME
          Bar nextBar2 = barSet2.getBar(nextIndex);  // 交易 DATA_FILENAME2
          Bar breakBar2 = barSet2.getBar(nextIndex - 1);

          // double liquidatePrice = nextBar.open; // 假設隔天開盤價成交(空單平倉)
          double liquidatePrice = nextBar.close; // 假設隔天收盤價成交(空單平倉)******* 多賺許多
          double liquidatePrice2 = nextBar2.close;
          
          // if (liquidatePrice < basePrice + lossStopForShort) { //
          // 停損出場當天要平倉前變成正獲利，就不平倉了，繼續持有。
          // **************************************** 這要盯收盤，不太符合本程式精神
          // *******************
          // inHand = true;
          // continue;
          // }

//          double temp = nextBar.open - nextBar.close;  // ********* 停損空單平倉訊號指示出場當天，開盤加碼放空，收盤平倉所有部位 *************** 有賺頭，逾 6 成勝率
          double temp2 = nextBar2.open - nextBar2.close;  // ********* 停損空單平倉訊號指示出場當天，開盤加碼放空，收盤平倉所有部位 *************** 有賺頭，逾 6 成勝率
//          accuProfit += temp;
          accuProfit += temp2;
//          scProfitShortCloseDayTrade.enter(temp);
          scProfitShortCloseDayTrade.enter(temp2);

//          double profit = basePrice - liquidatePrice; // 計算獲利
          double profit2 = basePrice2 - liquidatePrice2; // 計算獲利
//          accuProfit += profit;
          accuProfit += profit2;
//          scProfit.enter(profit);
          scProfit.enter(profit2);
//          scProfitShort.enter(profit);
          scProfitShort.enter(profit2);

          if (IS_PRINT_TURTLE_SUMMARY) {
            scHoldCalendarDays.enter(diffCalendarDays(nextBar, baseBar));
            scHoldTransactionDays.enter(it.nextIndex() - baseBarIndex);
          }
          
//          printTurtleAction(nextBar, inHand, bull, profit, daysOut, true, lossStopForShort, liquidatePrice);
          printTurtleAction(nextBar, inHand, bull, profit2, daysOutForShort, true, lossStopForShort, liquidatePrice2, nextBar2, breakBar2);

          // if (listHighIn.size() == daysIn
          // && highestIndex(listLowIn) == daysIn - 1 // day bar 的 low 創 daysIn
          // 天新高
          // ) {
          // inHand = true; // 進場
          // bull = true; // 建立 long position
          // basePrice = nextBar.close; // 假設隔天收盤價成交
          // baseBar = nextBar;
          // baseBarIndex = it.nextIndex();
          // printTurtleAction(nextBar, inHand, bull, basePrice, daysIn, false,
          // 0, 0);
          // }
        } else if (bar.close <= basePrice - lossStopForLong // 停損
            && bull) { // 手上持有 long position = 做多
          inHand = false; // 出場
          int nextIndex = it.nextIndex();
          if (nextIndex == barSet.size())
            continue; // 解決在資料尾端會造成 IndexOutOfBoundsException 的問題
          
          Bar nextBar = barSet.getBar(nextIndex);  // 交易 DATA_FILENAME
          Bar nextBar2 = barSet2.getBar(nextIndex);  // 交易 DATA_FILENAME2
          Bar breakBar2 = barSet2.getBar(nextIndex - 1);

          // double liquidatePrice = nextBar.open; // 假設隔天開盤價成交(多單平倉) ******
          // 差異不大
          double liquidatePrice = nextBar.close; // 假設隔天收盤價成交(多單平倉)
          double liquidatePrice2 = nextBar2.close; // 假設隔天收盤價成交(多單平倉)

          // accuProfit += (nextBar.close - nextBar.open); // *********
          // 停損多單平倉訊號指示出場當天，開盤加碼做多，收盤平倉所有部位 *************** 沒賺頭
          // scProfitLongCloseDayTrade.enter(nextBar.close - nextBar.open);

          accuProfit += (nextBar2.close - nextBar2.open); // *********
          // 停損多單平倉訊號指示出場當天，開盤加碼做多，收盤平倉所有部位 *************** 沒賺頭
          scProfitLongCloseDayTrade.enter(nextBar2.close - nextBar2.open);

          // if (liquidatePrice > basePrice - lossStopForLong ) { //
          // 停損出場當天要平倉前變成正獲利，就不平倉了，繼續持有。
          // **************************************** 這要盯收盤，不太符合本程式精神
          // *******************
          // inHand = true;
          // continue;
          // }

//          double profit = liquidatePrice - basePrice; // 計算獲利
          double profit2 = liquidatePrice2 - basePrice2; // 計算獲利
//          accuProfit += profit;
          accuProfit += profit2;
//          scProfit.enter(profit);
          scProfit.enter(profit2);
//          scProfitLong.enter(profit);
          scProfitLong.enter(profit2);

          if (IS_PRINT_TURTLE_SUMMARY) {
            scHoldCalendarDays.enter(diffCalendarDays(nextBar, baseBar));
            scHoldTransactionDays.enter(it.nextIndex() - baseBarIndex);
          }
          
//          printTurtleAction(nextBar, inHand, bull, profit, daysOut, true, lossStopForLong, liquidatePrice);
          printTurtleAction(nextBar, inHand, bull, profit2, daysOutForLong, true, lossStopForLong, liquidatePrice2, nextBar2, breakBar2);
        }
      }
    }
    
    StringBuffer buf = new StringBuffer();
    String line;
    if (IS_PRINT_TURTLE_SUMMARY) {
      setAcc(scProfit.getSum());
      setProfitStat(scProfit);
      setMDD(scProfit.maxDrawDown);
      line = String.format(
                  "acc=%.0f[多%d進,%d出][空%d進,%d出][停%d,%d],%d筆交易(%dW/%dL,勝率%.3f),\nave持有天數=%.1f(共 %.0f)日曆天=%.1f(共 %.0f)交易日,ave每筆交易獲利=%.1f(%.1fW/%.1fL),\nmin持有天數=%.0f日曆天=%.0f交易日,max持有天數=%.0f日曆天=%.0f交易日,\nmax單筆獲利=%.0f,min單筆獲利=%.0f,持有天數std=%.1f日曆天=%.1f交易日,單筆獲利std=%.1f",
                  scProfit.getSum(), daysInForLong, daysOutForLong, daysInForShort, daysOutForShort, lossStopForLong, lossStopForShort,
                  scHoldCalendarDays.getCount(), scProfit.getPositiveCount(),
                  scProfit.getNegativeCount(),
                  (scProfit.getPositiveCount() + 0.0) / scProfit.getCount(),
                  scHoldCalendarDays.getMean(), scHoldCalendarDays.getSum(),
                  scHoldTransactionDays.getMean(), scHoldTransactionDays
                      .getSum(), scProfit.getMean(),
                  scProfit.getPositiveMean(), scProfit.getNegativeMean(),
                  scHoldCalendarDays.getMin(), scHoldTransactionDays.getMin(),
                  scHoldCalendarDays.getMax(), scHoldTransactionDays.getMax(),
                  scProfit.getMax(), scProfit.getMin(), 
                  scHoldCalendarDays.getStandardDeviation(), 
                  scHoldTransactionDays.getStandardDeviation(), 
                  scProfit.getStandardDeviation());
      buf.append(line+"\n");
      //System.out.println(line);
    }
    
    line = (String.format(
                "多單accLong獲利=%.0f ,共%d筆交易(%dW/%dL,勝率%.3f),ave每筆交易獲利=%.1f(%.1fW/%.1fL),max單筆獲利=%.0f,min單筆獲利=%.0f,單筆獲利std=%.1f",
                scProfitLong.getSum(), scProfitLong.getCount(), scProfitLong.getPositiveCount(), scProfitLong.getNegativeCount(),
                (scProfitLong.getPositiveCount() + 0.0) / scProfitLong.getCount(),
                scProfitLong.getMean(), scProfitLong.getPositiveMean(),
                scProfitLong.getNegativeMean(), scProfitLong.getMax(),
                scProfitLong.getMin(), scProfitLong.getStandardDeviation()));
    buf.append(line+"\n");
    //System.out.println(line);

    line = (String.format(
                "空單accShort獲利=%.0f ,共%d筆交易(%dW/%dL,勝率%.3f),ave每筆交易獲利=%.1f(%.1fW/%.1fL),max單筆獲利=%.0f,min單筆獲利=%.0f,單筆獲利std=%.1f",
                scProfitShort.getSum(), scProfitShort.getCount(), scProfitShort.getPositiveCount(), scProfitShort.getNegativeCount(),
                (scProfitShort.getPositiveCount() + 0.0) / scProfitShort.getCount(),
                scProfitShort.getMean(), scProfitShort.getPositiveMean(), 
                scProfitShort.getNegativeMean(), scProfitShort.getMax(), 
                scProfitShort.getMin(), scProfitShort.getStandardDeviation()));
    buf.append(line+"\n");
    //System.out.println(line);

    line = (String.format(
                "多單建立日accLongDayTrade獲利(開盤空、收盤補並做多)=%.0f ,共%d筆交易(%dW/%dL,勝率%.3f),ave每筆交易獲利=%.1f(%.1fW/%.1fL),max單筆獲利=%.0f,min單筆獲利=%.0f,單筆獲利std=%.1f",
                scProfitLongDayTrade.getSum(), scProfitLongDayTrade.getCount(),
                scProfitLongDayTrade.getPositiveCount(), scProfitLongDayTrade.getNegativeCount(), 
                (scProfitLongDayTrade.getPositiveCount() + 0.0) / scProfitLongDayTrade.getCount(), 
                scProfitLongDayTrade.getMean(), scProfitLongDayTrade.getPositiveMean(),
                scProfitLongDayTrade.getNegativeMean(), scProfitLongDayTrade.getMax(),
                scProfitLongDayTrade.getMin(),
                scProfitLongDayTrade.getStandardDeviation()));
    buf.append(line+"\n");
    //System.out.println(line);

    line = (String.format(
                "空單建立日accShortDayTrade獲利(開盤加倍空、收盤補一半)=%.0f ,共%d筆交易(%dW/%dL,勝率%.3f),ave每筆交易獲利=%.1f(%.1fW/%.1fL),max單筆獲利=%.0f,min單筆獲利=%.0f,單筆獲利std=%.1f",
                scProfitShortDayTrade.getSum(), scProfitShortDayTrade.getCount(), 
                scProfitShortDayTrade.getPositiveCount(),
                scProfitShortDayTrade.getNegativeCount(),
                (scProfitShortDayTrade.getPositiveCount() + 0.0) / scProfitShortDayTrade.getCount(), 
                scProfitShortDayTrade.getMean(), scProfitShortDayTrade.getPositiveMean(),
                scProfitShortDayTrade.getNegativeMean(),
                scProfitShortDayTrade.getMax(), 
                scProfitShortDayTrade.getMin(),
                scProfitShortDayTrade.getStandardDeviation()));
    buf.append(line+"\n");
    //System.out.println(line);

    line = (String.format(
                "多單停損日當沖accLongClosDayTrade獲利=%.0f ,共%d筆交易(%dW/%dL,勝率%.3f),ave每筆交易獲利=%.1f(%.1fW/%.1fL),max單筆獲利=%.0f,min單筆獲利=%.0f,單筆獲利std=%.1f", 
                scProfitLongCloseDayTrade.getSum(),
                scProfitLongCloseDayTrade.getCount(),
                scProfitLongCloseDayTrade.getPositiveCount(),
                scProfitLongCloseDayTrade.getNegativeCount(),
                (scProfitLongCloseDayTrade.getPositiveCount() + 0.0) / scProfitLongCloseDayTrade.getCount(),
                scProfitLongCloseDayTrade.getMean(),
                scProfitLongCloseDayTrade.getPositiveMean(),
                scProfitLongCloseDayTrade.getNegativeMean(),
                scProfitLongCloseDayTrade.getMax(), 
                scProfitLongCloseDayTrade.getMin(),
                scProfitLongCloseDayTrade.getStandardDeviation()));
    buf.append(line+"\n");
    //System.out.println(line);

    line = (String.format(
                "空單停損日當沖accShortCloseDayTrade獲利(開盤加碼空、收盤全平)=%.0f ,共%d筆交易(%dW/%dL,勝率%.3f),ave每筆交易獲利=%.1f(%.1fW/%.1fL),max單筆獲利=%.0f,min單筆獲利=%.0f,單筆獲利std=%.1f",
                scProfitShortCloseDayTrade.getSum(), 
                scProfitShortCloseDayTrade.getCount(),
                scProfitShortCloseDayTrade.getPositiveCount(),
                scProfitShortCloseDayTrade.getNegativeCount(),
                (scProfitShortCloseDayTrade.getPositiveCount() + 0.0) / scProfitShortCloseDayTrade.getCount(),
                scProfitShortCloseDayTrade.getMean(),
                scProfitShortCloseDayTrade.getPositiveMean(),
                scProfitShortCloseDayTrade.getNegativeMean(),
                scProfitShortCloseDayTrade.getMax(), 
                scProfitShortCloseDayTrade.getMin(), 
                scProfitShortCloseDayTrade.getStandardDeviation()));
    buf.append(line+"\n");
    //System.out.println(line);

    line = (String.format("accuProfit = %.0f", accuProfit)); 
    buf.append(line+"\n");
    //System.out.println(line);
    logger.info("\n"+buf.toString());
    summary = buf.toString();         
    return accuProfit;
  }
  private static Logger loadDefaultLogger() {
	    Properties logp = new Properties();
	    try {
	      logp.load(PriceData.class.getClassLoader()
	        .getResourceAsStream( "turtle/system/pt/j4util.properties"));
	    } catch (IOException e) {
	      e.printStackTrace();
	    }	    
	    PropertyConfigurator.configure(logp);
	    
	    return Logger.getRootLogger();	   
}
}